package handler

import (
	"context"
	"fmt"
	"github.com/golang/protobuf/proto"
	"github.com/golang/protobuf/ptypes/empty"
	"github.com/golang/protobuf/ptypes/wrappers"
	"math"

	"kop/framework/broker"
	context2 "kop/framework/context"
	"kop/framework/errors"
	"kop/libs/database"
	"kop/libs/redis"
	"kop/modules/captain/configuration"
	"kop/modules/captain/configuration/class"
	"kop/modules/captain/configuration/fetter"
	"kop/modules/captain/models"
	"kop/modules/configuration/configuration/attribute"
	"kop/modules/configuration/configuration/attribute/attrtype"
	"kop/modules/item/consts"
	"kop/modules/quest/configuration/questtype"
	"kop/pb"
	"kop/pb/service"
	"kop/util/rand"

	achievementC "kop/modules/achievement/client"
	activityC "kop/modules/activity/client"
	buildingC "kop/modules/building/client"
	itemC "kop/modules/item/client"
	maidM "kop/modules/maid/models"
	questC "kop/modules/quest/client"
	roleC "kop/modules/role/client"
)

type CaptainServiceHandler struct {
	context2.Context
}

func (h *CaptainServiceHandler) Piece(ctx context.Context, req *wrappers.Int32Value, resp *pb.CaptainPieceResp) error {

	var captainID = req.Value - 103000
	var conf, err = configuration.GetByCaptainID(captainID)
	if err != nil {
		return err
	}

	var roleID, serverID = h.GetRoleID(ctx), h.GetServerID(ctx)

	// del item
	if resp.Item, err = itemC.DelItem(roleID, serverID, req.Value, conf.ItemNumber); err != nil {
		return err
	}

	var data = models.Create(database.DB, roleID, conf.CaptainID)

	var c = models.New(roleID)
	resp.Captain = c.ToMessage(data.CaptainID)

	// 更新对应的羁绊
	updateForFetter(c, data.CaptainID)

	var role = roleC.GetRole(roleID)
	// 招募船长：提督[提督名称七个字]礼贤下士，招募到了[船长名称]（对应品质颜色）
	_ = broker.ServerPublish(serverID, pb.WsCode_CodeMessage, &pb.ServerMessage{Type: 2, Name: []string{role.Name}, Nums: []int32{captainID}})

	questC.CaptainLevel(roleID, captainID, 1)
	go achievementC.CaptainNum(roleID)
	return nil
}

func (h *CaptainServiceHandler) Create(ctx context.Context, req *wrappers.Int32Value, resp *pb.CreateCaptainResp) error {

	var roleID, captainID = h.GetRoleID(ctx), req.Value
	var conf, err = configuration.GetByCaptainID(captainID)
	if err != nil {
		return err
	}

	if _, err = models.GetCaptain(database.DB, roleID, captainID); err == nil {
		// 已有船长 转成碎片

		var rawItem = pb.RawItem{ItemID: conf.ItemID, Number: conf.ItemNumber}
		var item = itemC.AddItem(roleID, rawItem.ItemID, rawItem.Number)
		resp.CaptainOrPiece = &pb.CreateCaptainResp_AwardItem{AwardItem: &pb.AwardItem{RawItem: &rawItem, Item: item}}

	} else {

		var data = models.Create(database.DB, roleID, captainID)
		var c = models.New(roleID)

		resp.CaptainOrPiece = &pb.CreateCaptainResp_Captain{Captain: c.ToMessage(data.CaptainID)}

		// 更新对应的羁绊
		updateForFetter(c, data.CaptainID)

		var role = roleC.GetRole(roleID)
		// 招募船长：提督[提督名称七个字]礼贤下士，招募到了[船长名称]（对应品质颜色）
		_ = broker.ServerPublish(h.GetServerID(ctx), pb.WsCode_CodeMessage, &pb.ServerMessage{Type: 2, Name: []string{role.Name}, Nums: []int32{captainID}})
		go achievementC.CaptainNum(roleID)
	}

	questC.CaptainLevel(roleID, captainID, 1)
	return nil
}

func (h *CaptainServiceHandler) GetCaptains(_ context.Context, req *service.RoleRequest, resp *service.Captains) error {

	resp.Captains = models.New(req.RoleID).ToPbCaptains(nil)
	return nil
}

// 合成魔戒时检查是否有装备在船长身上
func (h *CaptainServiceHandler) GetRingUniqueIDs(_ context.Context, req *service.RoleRequest, resp *pb.Int32Slice) error {

	resp.Slices = make([]int32, 0)

	var data = models.New(req.RoleID).Captains
	for _, v := range data {
		resp.Slices = append(resp.Slices, v.RingUniqueID)
	}

	return nil
}

//func (h *CaptainServiceHandler) GetCaptain(_ context.Context, req *service.DefaultRequest, resp *service.Captain) error {
//
//	var captain, err = models.GetCaptain(database.DB, req.RoleID, req.Value)
//	if err == nil {
//		var mess = models.BuildPbCaptain(captain, nil)
//		resp.Captain = &mess
//	}
//
//	return nil
//}

func (h *CaptainServiceHandler) GetCaptainsByReady(_ context.Context, req *service.RoleRequest, resp *service.Captains) error {

	var c = models.New(req.RoleID)

	resp.Captains = c.ToPbCaptains(c.GetByReady())
	return nil
}

//func (h *CaptainServiceHandler) CountByLevel(_ context.Context, req *service.DefaultRequest, resp *wrappers.Int32Value) error {
//
//	resp.Value = models.CountByLevel(database.DB, req.RoleID, req.Value)
//	return nil
//}
//
//func (h *CaptainServiceHandler) CountByClass(_ context.Context, req *service.DefaultRequest, resp *wrappers.Int32Value) error {
//
//	resp.Value = models.CountByClass(database.DB, req.RoleID, req.Value)
//	return nil
//}

func (h *CaptainServiceHandler) LevelUp(ctx context.Context, req *wrappers.Int32Value, resp *pb.Captain) error {

	var roleID = h.GetRoleID(ctx)

	var c = models.New(roleID)
	var captain = c.GetEntity(req.Value)

	classConf, err := class.GetByCaptainClass(captain.Class)
	if err != nil {
		return err
	}

	if captain.Level >= classConf.CaptainLevelMax {
		return errors.ParamError
	}

	captainConf, err := configuration.GetByCaptainID(captain.CaptainID)
	if err != nil {
		return err
	}

	attrConf, err := attribute.GetByAttributeID(captainConf.AttributeID + captain.Level)
	if err != nil {
		return err
	}

	if err = itemC.AsyncDelItem(roleID, h.GetServerID(ctx), consts.Gold, attrConf.MoneyCost); err != nil {
		return err
	}

	var upLevel int32 = 1
	var n = redis.GetTodayNumber(fmt.Sprintf("CaptainLevelUpLuck%d", roleID))
	var luck = n < int(roleC.GetVIP(roleID).Benediction)
	if captain.Level < 100 && luck && rand.IsWin(0.15) {

		upLevel = 3
		redis.SetTodayNumber(fmt.Sprintf("CaptainLevelUpLuck%d", roleID), n+1)
	}

	captain.Level += upLevel
	models.Save(database.DB, captain)

	if captain.BuildingID > 0 {
		go buildingC.ResetCoin(ctx, captain.BuildingID, c.ToPbCaptains(nil))
	}

	*resp = *c.ToMessage(captain.CaptainID)

	// quest , daily quest
	go h.updatePower(roleID, c)
	questC.Check(roleID, questtype.CaptainLevelNum, questtype.CaptainLevel)
	questC.DailyQuestCaptainLevelUp(roleID)
	return nil
}

func (h *CaptainServiceHandler) SkillLevelUp(_ context.Context, req *service.CaptainRequest, resp *pb.Captain) error {

	if req.Value != 2 && req.Value != 3 {
		return errors.ParamError
	}

	var captainConf, err = configuration.GetByCaptainID(req.CaptainID)
	if err != nil {
		return err
	}

	skill2Conf, err := configuration.GetBySkill2ID(captainConf.Skill2)
	if err != nil {
		return err
	}

	var c = models.New(req.RoleID)
	var captain = c.GetEntity(req.CaptainID)

	var needExp int32 = 0

	switch req.Value {

	case 2:

		if captain.Skill2Level >= int32(math.Min(float64(skill2Conf.GradeMax), float64(captain.Level))) {
			return errors.ParamError
		}

		attrConf, err := attribute.GetByAttributeID(skill2Conf.AttributeID + captain.Skill2Level)
		if err != nil {
			return err
		}
		needExp = attrConf.ExpCost

		captain.Skill2Level += 1
	case 3:

		if captain.Skill3Level >= int32(math.Min(float64(skill2Conf.GradeMax), float64(captain.Level))) {
			return errors.ParamError
		}

		attrConf, err := attribute.GetByAttributeID(skill2Conf.AttributeID + captain.Skill3Level)
		if err != nil {
			return err
		}
		needExp = attrConf.ExpCost

		captain.Skill3Level += 1
	}

	if captain.Exp < needExp {
		return errors.ParamError
	}

	captain.Exp -= needExp
	models.Save(database.DB, captain)

	*resp = *c.ToMessage(captain.CaptainID)
	return nil
}

func (h *CaptainServiceHandler) ClassUp(ctx context.Context, req *wrappers.Int32Value, resp *pb.Captain) error {

	var roleID = h.GetRoleID(ctx)

	var c = models.New(roleID)
	var captain = c.GetEntity(req.Value)

	classConf, err := class.GetByCaptainClass(captain.Class)
	if err != nil {
		return err
	}

	if len(classConf.Items) == 0 {
		return errors.ParamError
	}

	if captain.Level < classConf.CaptainLevelMax {
		return errors.ParamError
	}

	var prepares = make([]*itemC.Prepare, 0, len(classConf.Items))
	for _, itemID := range classConf.Items {
		// 检查道具数量
		var prepare, err = itemC.NewPrepare(roleID, h.GetServerID(ctx)).Prepare(itemID, 1)
		if err != nil {
			return err
		}
		prepares = append(prepares, prepare)
	}

	for _, prepare := range prepares {
		_ = prepare.AsyncRun()
	}

	captain.Class += 1
	models.Save(database.DB, captain)

	if captain.BuildingID > 0 {
		go buildingC.ResetCoin(ctx, captain.BuildingID, c.ToPbCaptains(nil))
	}

	*resp = *c.ToMessage(captain.CaptainID)

	// 主线任务
	questC.CaptainClass(roleID, models.CountByClass(database.DB, roleID, captain.Class), captain.Class)

	var role = roleC.GetRole(roleID)
	// 船长爵位晋升：提督[提督名称七个字]旗下船长[船长名称]被晋升为[爵位名称]！
	_ = broker.ServerPublish(h.GetServerID(ctx), pb.WsCode_CodeMessage, &pb.ServerMessage{Type: 3, Name: []string{role.Name}, Nums: []int32{captain.CaptainID, captain.Class}})
	return nil
}

func (h *CaptainServiceHandler) Equip(ctx context.Context, req *service.CaptainEquipRequest, resp *pb.Captain) (err error) {

	var roleID = h.GetRoleID(ctx)

	var c = models.New(roleID)
	var captain = c.GetEntity(req.CaptainID)

	var ringUniqueID = req.Ring.ID

	if captain.RingUniqueID == ringUniqueID {

	} else if ringUniqueID == 0 {

		captain.RemoveRing()
		models.Save(database.DB, captain)

		if captain.BuildingID > 0 {
			go buildingC.ResetCoin(ctx, captain.BuildingID, c.ToPbCaptains(nil))
		}

	} else {

		if captain2 := c.GetByRingUniqueID(ringUniqueID); captain2 != nil {
			// 先从captain2身上取下戒指
			captain2.RemoveRing()
			models.Save(database.DB, captain2)

			go func() {

				defer func() {
					_ = recover()
				}()

				_ = broker.RolePublish(roleID, pb.WsCode_CodeCaptain, c.ToMessage(captain2.CaptainID))

				if captain2.BuildingID > 0 {
					buildingC.ResetCoin(ctx, captain2.BuildingID, c.ToPbCaptains(nil))
				}
			}()
		}

		captain.EquipRing(req.Ring)
		models.Save(database.DB, captain)

		if captain.BuildingID > 0 {
			go buildingC.ResetCoin(ctx, captain.BuildingID, c.ToPbCaptains(nil))
		}
	}

	*resp = *c.ToMessage(captain.CaptainID)

	go h.updatePower(roleID, c)
	return nil
}

func (h *CaptainServiceHandler) AddExp(_ context.Context, req *service.CaptainRequest, resp *pb.Captain) error {

	var c = models.New(req.RoleID)
	var captain = c.GetEntity(req.CaptainID)

	if req.Value < 1 {
		return errors.ParamError
	}

	captain.Exp += req.Value
	models.Save(database.DB, captain)

	*resp = *c.ToMessage(captain.CaptainID)
	return nil
}

func (h *CaptainServiceHandler) Ready(_ context.Context, req *service.CaptainReadyRequest, resp *pb.Captain) error {

	var c = models.New(req.RoleID)
	var captain = c.GetEntity(req.CaptainID)

	if captain.Ready == req.Ready {
		*resp = *c.ToMessage(captain.CaptainID)
		return nil
	}

	if !req.Ready {
		// 至少保留25个上阵船长
		var i int
		database.DB.Table(captain.TableName()).Where("role_id = ? AND ready = ?", req.RoleID, true).Count(&i)
		if i <= 25 {
			return errors.ParamError
		}
	}

	captain.Ready = req.Ready
	models.Save(database.DB, captain)

	*resp = *c.ToMessage(captain.CaptainID)

	// 更新对应的羁绊
	updateForFetter(c, captain.CaptainID)
	return nil
}

//func (h *CaptainServiceHandler) AsyncSaveMaid(ctx context.Context, maid *pb.Maid, _ *empty.Empty) error {
//
//	var roleID = h.GetRoleID(ctx)
//	var c = models.New(roleID)
//
//	if captain := c.GetByMaidID(maid.MaidID); captain != nil {
//
//		captain.Maid, _ = proto.Marshal(maid)
//		models.Save(database.DB, captain)
//
//		_ = broker.RolePublish(roleID, pb.WsCode_CodeCaptain, c.ToMessage(captain.CaptainID))
//
//		if captain.BuildingID > 0 {
//			go buildingC.ResetCoin(ctx, captain.BuildingID, c.ToPbCaptains(nil))
//		}
//
//		go h.updatePower(roleID, c)
//	}
//
//	return nil
//}

func (h *CaptainServiceHandler) AsyncSaveRing(ctx context.Context, req *service.CaptainEquipRequest, _ *empty.Empty) error {

	var roleID = h.GetRoleID(ctx)
	var c = models.New(roleID)

	if captain := c.GetByRingUniqueID(req.Ring.ID); captain != nil {

		captain.RingINT = int32(float64(req.Ring.Attribute.INT) / 10000 * float64(req.Ring.UpValue+req.Ring.UpValue2+10000))
		captain.RingCOR = int32(float64(req.Ring.Attribute.COR) / 10000 * float64(req.Ring.UpValue+req.Ring.UpValue2+10000))
		captain.RingCHR = int32(float64(req.Ring.Attribute.CHR) / 10000 * float64(req.Ring.UpValue+req.Ring.UpValue2+10000))

		models.Save(database.DB, captain)

		_ = broker.RolePublish(roleID, pb.WsCode_CodeCaptain, c.ToMessage(captain.CaptainID))

		if captain.BuildingID > 0 {
			if req.Ring.Attribute.CHR > 0 {
				go buildingC.ResetCoin(ctx, captain.BuildingID, c.ToPbCaptains(nil))
			}
		}

		go h.updatePower(roleID, c)
	}

	return nil
}

func (h *CaptainServiceHandler) SetBuildingID(_ context.Context, req *service.SetBuildingIDRequest, _ *empty.Empty) error {

	return models.SetBuildingID(database.DB, req.RoleID, req.CaptainID, req.BuildingID)
}

func (h *CaptainServiceHandler) TalentLevelUp(ctx context.Context, req *pb.TalentLevelUpRequest, resp *pb.TalentLevelUpResp) error {

	var roleID = h.GetRoleID(ctx)

	var c = models.New(roleID)
	var captain = c.GetEntity(req.CaptainID)

	var lv, ok = captain.TalentMap[req.TalentID]
	if !ok {
		return errors.ParamError
	}

	talentConf, err := configuration.GetByTalentID(req.TalentID)
	if err != nil {
		return err
	}

	classConf, err := class.GetByCaptainClass(captain.Class)
	if err != nil {
		return err
	}

	if lv >= classConf.TalentLevelMax {
		return errors.ParamError
	}

	var itemNumber int32 = 1
	if req.ItemID == talentConf.ItemID {

		if resp.Item, err = itemC.DelItem(roleID, h.GetServerID(ctx), req.ItemID, 1); err != nil {
			return err
		}

		// 卷轴升级
		if !rand.IsWin(talentConf.Probability) {
			resp.Success = false
			return nil
		}

		// quest of activity
		activityC.AddPoints(roleID, h.GetServerID(ctx), activityC.CaptainTalent, 1)

	} else {
		// 碎片升级
		var conf configuration.CaptainConf

		for _, v := range configuration.CaptainConfigs {
			if v.ItemID == req.ItemID {
				conf = v
			}
		}

		if conf.CaptainID == 0 {
			return errors.ParamError
		}

		itemNumber = talentConf.PieceCount

		if resp.Item, err = itemC.DelItem(roleID, h.GetServerID(ctx), req.ItemID, itemNumber); err != nil {
			return err
		}

	}

	captain.TalentMap[req.TalentID] += 1
	models.Save(database.DB, captain)

	if captain.BuildingID > 0 {

		if talentConf.Type == attrtype.TypeCHR {
			go buildingC.ResetCoin(ctx, captain.BuildingID, c.ToPbCaptains(nil))
		}
	}

	resp.Success = true
	resp.Captain = c.ToMessage(captain.CaptainID)

	// quest main
	//questC.Power(roleID)

	return nil
}

func (h *CaptainServiceHandler) Maid(ctx context.Context, req *pb.CaptainAPIMaidRequest, resp *pb.Captain) (err error) {

	var roleID = h.GetRoleID(ctx)

	var c = models.New(roleID)
	var captain = c.GetEntity(req.CaptainID)

	var maid = maidM.GetMaid(database.DB, roleID, req.MaidID)
	if !maid.IsFriend {
		return errors.ParamError
	}

	if captain.MaidID == req.MaidID {
		return nil
	}

	var session = database.DB.Begin()

	defer func() {
		if r := recover(); r != nil {

			err = r.(error)
		}
		if err != nil {
			session.Rollback()
		}
	}()

	if captain2 := c.GetByMaidID(maid.MaidID); captain2 != nil {

		captain2.MaidID = 0
		captain2.Maid = []byte{}
		models.Save(session, captain2)

		if captain2.BuildingID > 0 {
			go buildingC.ResetCoin(ctx, captain2.BuildingID, c.ToPbCaptains(nil))
		}

		_ = broker.RolePublish(roleID, pb.WsCode_CodeCaptain, c.ToMessage(captain2.CaptainID))
	}

	captain.MaidID = maid.MaidID
	captain.Maid, _ = proto.Marshal(&maid)

	models.Save(session, captain)

	if err = session.Commit().Error; err != nil {
		return err
	}

	go h.updatePower(roleID, c)
	if captain.BuildingID > 0 {
		go buildingC.ResetCoin(ctx, captain.BuildingID, c.ToPbCaptains(nil))
	}

	*resp = *c.ToMessage(captain.CaptainID)
	return nil
}

func (h *CaptainServiceHandler) UpdateByCamp(ctx context.Context, req *wrappers.Int32Value, _ *empty.Empty) error {

	var roleID = h.GetRoleID(ctx)

	var c = models.New(roleID)
	for _, v := range c.GetByReady() {

		var conf, _ = configuration.GetByCaptainID(v.CaptainID)
		if conf.Camp == req.Value {
			_ = broker.RolePublish(roleID, pb.WsCode_CodeCaptain, c.ToMessage(v.CaptainID))
		}
	}

	return nil
}

// 更新主角实力
func (h *CaptainServiceHandler) updatePower(roleID int32, c *models.C) {

	defer func() {
		_ = recover()
	}()

	var captains = c.ToPbCaptains(c.GetByReady())

	var power int64
	for _, v := range captains {
		power += v.Power
	}

	roleC.UpdatePower(roleID, power)

}

// 新增 | 下阵 更新船长羁绊
func updateForFetter(captains *models.C, captainID int32) {

	defer func() {
		_ = recover()
	}()

	var fetters = make(map[int32]bool)
	for _, v := range fetter.Configs {
		if v.CaptainID1 == captainID || v.CaptainID2 == captainID || v.CaptainID3 == captainID {
			fetters[v.ID] = true
		}
	}

	for _, v := range captains.GetByReady() {

		var conf, _ = configuration.GetByCaptainID(v.CaptainID)
		for _, ID := range conf.FetterCaptains {
			if _, ok := fetters[ID]; ok {

				_ = broker.RolePublish(v.RoleID, pb.WsCode_CodeCaptain, captains.ToMessage(v.CaptainID))
				break
			}
		}
	}
}
